组件中使用 useWs
<!-- App.vue -->
<template>
<div>
<button @click="init">Init</button>
<button @click="handleSend">Send</button>
<button @click="handleClose">Close</button>
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { useWs } from './utils/use-ws'
const { init, send, close, message } = useWs({
url: 'ws://localhost:3031'
})
function handleSend() {
send({
event: 'message',
data: 'hello from client'
})
}
function handleClose() {
close()
}
</script>
vue
测试流程
单 Tab 基本功能测试
- 启动 NestJS WebSocket 服务端
- 打开浏览器页面,点击 "Init" 按钮初始化连接
- 在 SharedWorker 的调试窗口(
chrome://inspect-> Shared Workers)中观察事件消息 - 点击 "Send" 发送消息,确认服务端收到
- 确认页面收到服务端的响应消息
- 点击 "Close" 断开连接
断线重连测试
- 点击 "Init" 建立连接
- 停止服务端(Ctrl+C)
- 观察控制台输出:断线重连日志按策略间隔出现
- 重新启动服务端,确认客户端自动恢复连接
- 恢复后发送消息确认通信正常
多 Tab 共享测试
这是最关键的测试——验证多个 Tab 共享同一个 WebSocket 实例:
- 打开第一个 Tab,点击 "Init" 初始化连接
- 打开第二个 Tab(相同 URL),点击 "Init"
- 在 Network 面板中观察:只有一条 WebSocket 连接,第二个 Tab 没有创建新连接
- 在第一个 Tab 中点击 "Send",确认两个 Tab 都收到服务端响应
- 在第二个 Tab 中点击 "Send",确认消息发送成功
多 Port 消息广播的 Bug 修复
最初的实现中存在一个 Bug:onMessage 回调只将消息发送给最后一个 port,导致其他 Tab 收不到消息。修复方法是将 port 存入数组并遍历广播:
// 修复前:只发送给当前 port
onMessage: (event, msgData) => {
port.postMessage({ type: 'message', data: msgData })
}
// 修复后:广播给所有 port
if (!connections[id]) {
connections[id] = { client, ports: [port] }
} else {
connections[id].ports.push(port)
}
onMessage: (event, msgData) => {
const connection = connections[id]
connection.ports.forEach(p => {
p.postMessage({ type: 'message', data: msgData })
})
}
javascript
生产构建配置
SharedWorker 中引用的 websocket-client.global.js 需要在构建时被正确打包。在 package.json 中配置构建命令:
{
"scripts": {
"build:ws": "npx esbuild src/utils/websocket-client.ts --format=iife --outdir=src/utils",
"build": "npm run build:ws && vite build",
"dev:ws": "npx esbuild src/utils/websocket-client.ts --format=iife --outdir=src/utils --watch"
}
}
json
build:ws 将 TypeScript 编译为 IIFE 格式的 JS 文件,输出到 dist 目录的对应位置,确保 SharedWorker 在生产环境中能正确引用。
总结:WebSocket 连接管理的三层架构
第一层:基础工具类 WebSocketClient
├── 连接管理(connect/close)
├── 心跳检测(ping/pong)
├── 断线重连(reconnect/retry strategy)
└── 消息缓存(dataArr)
第二层:单例模式 getInstance
└── 同一 SPA 内共享实例
第三层:SharedWorker + useWs
├── 跨 Tab 共享实例
├── 多 Port 消息广播
└── 回调函数的间接传递
text
这种分层架构确保了从最基础的连接管理到最复杂的多 Tab 共享,每一层都可以独立使用和测试。在生产项目中,可以根据实际需求选择使用到哪一层。
↑